﻿using Avalon.Core.Data;
using Avalon.Core.MVC.Actions;
using Avalon.Core.MVC.Helpers;
using Avalon.Core.Providers;
using System;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace Avalon.Core.MVC
{

    public class AvalonActionInvoker<TModel> : IActionInvoker where TModel : class, IEntity, new()
    {
        private ControllerContext _controllerContext;

        public bool InvokeAction(ControllerContext controllerContext, string actionName)
        {
            _controllerContext = controllerContext;
            if (HttpContext.Current.Request.HttpMethod == "GET")
            {
                ExecuteGet();
            }
            else if (HttpContext.Current.Request.HttpMethod == "POST")
            {
                ExcutePost();
            }
            return true;
        }

        protected void ExecuteGet(object model = null)
        {
            //Grab the controller, renderer and entity descriptors
            IControllerDescriptor controllerDescriptor = ContextProvider.ControllerDescriptor;
            IRendererDescriptor rendererDescriptor = controllerDescriptor.RendererDescriptor;
            IEntityDescriptor entityDescriptor = controllerDescriptor.EntityDescriptor;

            if (model == null) {
                IPipelineProvider pipelineProvider = ProviderContainer.Current.PipelineProvider;
                PipelineDescriptor pipelineDescriptor = pipelineProvider.GetPipelineDescriptor(PipelineType.Get);
                if (pipelineDescriptor == null) throw new Exception("Pipeline descriptor is null");

                IPipeline pipeline = pipelineDescriptor.GetPipeline();
                pipeline.Execute();
                _controllerContext.Controller.ViewData.Model = pipeline.StateBag.Result;
                entityDescriptor.Model = pipeline.StateBag.Result;
            }
            else {
                _controllerContext.Controller.ViewData.Model = model;
                entityDescriptor.Model = model;
            }

            //Execute the view.
            IView view = new AvalonViewFactory().CreateAvalonView(_controllerContext, string.Empty, string.Empty);
            var viewContext = new ViewContext(_controllerContext, view, _controllerContext.Controller.ViewData, new TempDataDictionary(), System.Web.HttpContext.Current.Response.Output);
            view.Render(viewContext, System.Web.HttpContext.Current.Response.Output);
        }


        private void ExcutePost()
        {
            //Create the model from the values specified in the Params of the HttpContext.
            ModelBindingContext bindingContext = AvalonModelBinder.CreateBindingContext<TModel>(null);
            AvalonModelBinder modelBinder = new AvalonModelBinder();
            TModel modelParameter = (TModel)modelBinder.BindModel(_controllerContext, bindingContext);

            //Abort when the model is not valid
            if (!bindingContext.ModelState.IsValid)
            {
                ExecuteGet(modelParameter);
                return;
            }

            //Grab the corrisponding pipeline.
            IPipelineProvider pipelineProvider = ProviderContainer.Current.PipelineProvider;
            PipelineDescriptor pipelineDescriptor = pipelineProvider.GetPipelineDescriptor(PipelineType.Post);

            IPipeline pipeline = pipelineDescriptor.GetPipeline();
            pipeline.StateBag.ParameterValues.Add("Model", modelParameter);
            pipeline.Execute();

            navigate(modelParameter);
        }

        private void navigate(TModel model)
        {
            //If the model has a parent
            ModelMetadata parent = ModelMetadataProviders.Current.GetMetadataForType(() => model, typeof(TModel)).GetParentProperty();
            if (parent != null) {
                var uri = parent.GetEntityDetailUrl();
                new RedirectResult(uri.ToString()).ExecuteResult(_controllerContext);
            }
            else {
                //If a parent id is being send within the request paramers
                if (HttpContext.Current.Request.Params.AllKeys.Contains("parentId")) {
                    string controllerName = ControllerProvider.GetControllerName(HttpContext.Current.Request.Params["parentName"].ToString());
                    string actionName = "Detail";
                    string param = HttpContext.Current.Request.Params["parentId"];
                    RedirectResult redirectResult = new RedirectResult(string.Format("/{0}/{1}/{2}", controllerName, actionName, param));
                    redirectResult.ExecuteResult(_controllerContext);
                }
                //Just navigate to the list of these items
                else {
                    var controllerName = _controllerContext.RouteData.Values["controller"].ToString();
                    var redirectResult = new RedirectResult(string.Format("/{0}", controllerName));
                    redirectResult.ExecuteResult(_controllerContext);
                }
            }
        }
    }

    public class AvalonActionInvoker<TViewModel, TModel> : AvalonActionInvoker
        where TModel : class, IEntity, new()
        where TViewModel : IViewModel, new()
    {
    }


    public class AvalonActionInvoker : IActionInvoker {
        public bool InvokeAction(ControllerContext controllerContext, string actionName) {
            return true;
        }
    }

}
